from jira import JIRA, Issue, JIRAError
from typing import List, Optional, Union

ISSUE_TYPE_INTERNAL_FEATURE = 'Internal Feature'
ISSUE_TYPE_CUSTOMER_FEATURE = 'Customer Feature'
ISSUE_TYPE_SYSTEM_ITEM = 'System Item'
ISSUE_TYPE_ENTITY_ITEM = 'Entity Item'
ISSUE_TYPE_CA_ITEM = 'Competence Area'
ISSUE_TYPE_EPIC_ITEM = 'Epic'
ISSUE_TYPE_STA = 'System Technical Analysis'
ISSUE_TYPE_ETA = 'Entity Technical Analysis'
ISSUE_TYPE_EE = 'Effort Estimation'

TRANSITION_NAME_NEW_SI = 'New SI'
TRANSITION_NAME_NEW_EI = 'New EI'
TRANSITION_NAME_NEW_CA = 'New CA Item'

TRANSITION_NAME_STATUS_SCHEDULED = 'Planned'
TRANSITION_NAME_STATUS_INPROGRESS = 'In Progress'
TRANSITION_NAME_STATUS_DONE = 'Completed'
TRANSITION_NAME_STATUS_OPEN = 'Re-open'
TRANSITION_NAME_STATUS_OBSOLETE = 'Not Valid'

FB_NOT_COMMITTED = 'Not Committed'
FB_COMMITTED = 'Committed'

RELEASE_NOT_COMMITTED = 'Not Committed'
RELEASE_COMMIT_READY = 'Ready for Commitment'
RELEASE_COMMIT_AFTER = 'Committed after P2/C2'
RELEASE_COMMIT_AT = 'Committed at P2/C2'
RELEASE_COMMIT_BEFORE = 'Committed before P2/C2'

CA_RAN_SYSARCH = 'RAN SysArch'
CA_RAN_SYSSPEC_1 = 'RAN SysSpec1'
CA_RAN_SYSSPEC_2 = 'RAN SysSpec2'
CA_RAN_SYSSIMU = 'RAN SysSimu'

ACTIVITY_TYPE_CFAM = 'System Specification'
ACTIVITY_TYPE_FS1TA = 'Pre-anlaysis'

SUB_ACTIVITY_TYPE_FS1TA = 'Pre-analysis'
SUB_ACTIVITY_TYPE_CP1 = 'CP1'
SUB_ACTIVITY_TYPE_CP2 = 'CP2'
SUB_ACTIVITY_TYPE_SIMU = 'System Simulation'
SUB_ACTIVITY_TYPE_FSY = 'ARC'

FEATURE_BUILD_LIST = ['1910', '1910', '1911', '1911', '1912', '1912', '1913', '1913',
                      '2001', '2001', '2002', '2002', '2003', '2003', '2004', '2004', '2005', '2005', '2006', '2006',
                      '2007', '2007', '2008', '2008', '2009', '2009', '2010', '2010', '2011', '2011', '2012', '2012',
                      '2013', '2013',
                      '2101', '2101', '2102', '2102', '2103', '2103', '2104', '2104', '2105', '2105', '2106', '2106',
                      '2107', '2107', '2108', '2108', '2109', '2109', '2110', '2110', '2111', '2111', '2112', '2112',
                      '2113', '2113',
                      '2201', '2202', '2203', '2204', '2205', '2206', '2207', '2208', '2209', '2210', '2211', '2212',
                      '2213', '2214', '2215', '2216', '2217', '2218', '2219', '2220', '2221', '2222', '2223', '2224',
                      '2225', '2226',
                      '2301', '2302', '2303', '2304', '2305', '2306', '2307', '2308', '2309', '2310', '2311', '2312',
                      '2313', '2314', '2315', '2316', '2317', '2318', '2319', '2320', '2321', '2322', '2323', '2324',
                      '2325', '2326']


def logprint(*args, sep=' ', end='\n'):
    import datetime
    print(datetime.datetime.now(), *args, sep=sep, end=end)

def get_current_fb():
    import datetime
    fb1910 = datetime.datetime.strptime('2019-09-18T00:00:00', '%Y-%m-%dT%H:%M:%S')
    delta = datetime.datetime.now() - fb1910
    index = delta.days // 14
    return FEATURE_BUILD_LIST[index]


def get_next_fb():
    import datetime
    fb1910 = datetime.datetime.strptime('2019-09-18T00:00:00', '%Y-%m-%dT%H:%M:%S')
    delta = datetime.datetime.now() - fb1910
    index = (delta.days // 14) + 1
    return FEATURE_BUILD_LIST[index]


def convert_jira_date_to_fb(date_str: str):
    import datetime
    fb1910 = datetime.datetime.strptime('2019-09-18T00:00:00', '%Y-%m-%dT%H:%M:%S')
    date_cvt = datetime.datetime.strptime(date_str[:19], '%Y-%m-%dT%H:%M:%S')
    delta = date_cvt - fb1910
    index = delta.days // 14
    return FEATURE_BUILD_LIST[index]


STATUS_DONE = 'Done'
STATUS_OPEN = 'Open'
STATUS_OBSOLETE = 'Obsolete'
STATUS_SCHEDULED = 'Scheduled'
STATUS_INPROGRESS = 'In Progress'
STATUS_APPROVED_FOR_DEV = 'APPROVED FOR DEV'


def get_config_file():
    import os
    file = os.path.split(os.path.abspath(__file__))[0] + "/config.ini"
    return file


def load_jira_config():
    import configparser
    config = configparser.ConfigParser()
    config.read_file(open(get_config_file()))

    return config.get('Account', 'url'), config.get('Account', 'user'), config.get('Account', 'passwd')


def init_jira_connection() -> JIRA:
    s_url, s_user, s_passwd = load_jira_config()
    try:
        return JIRA(server=s_url, basic_auth=(s_user, s_passwd), max_retries=1)
    except JIRAError as e:
        print(e.text)
        options = {'verify': False}
        return JIRA(server=s_url, basic_auth=(s_user, s_passwd), options=options)


def str_l(s_list):
    if s_list:
        return ",".join(str(i) for i in s_list)


class UserInfo(object):
    def __init__(self, raw_data):
        self.display_name = raw_data['displayName']
        self.name = raw_data['name']
        self.key = raw_data['key']
        self.email = raw_data['emailAddress']
        self.active = raw_data['active']

    def __str__(self):
        return str({
            'name': self.name,
            'displayName': self.display_name,
            'email': self.email
        })


__PRINT_IDENT__ = {
    ISSUE_TYPE_INTERNAL_FEATURE: "",
    ISSUE_TYPE_CUSTOMER_FEATURE: "",
    ISSUE_TYPE_SYSTEM_ITEM: "   ",
    ISSUE_TYPE_STA: "   ",
    ISSUE_TYPE_ENTITY_ITEM: "      ",
    ISSUE_TYPE_ETA: "      ",
    ISSUE_TYPE_CA_ITEM: "         ",
    ISSUE_TYPE_EE: "         ",
    ISSUE_TYPE_EPIC_ITEM: "            ",
}


class Item(object):
    def __init__(self, issue: Issue, jira_op=None):
        self.fields = issue.fields
        self.issue = issue
        self.id: int = int(issue.id)
        self.key: str = issue.key
        self.jira_op = jira_op
        self.link = issue.permalink()

    def __str__(self):
        return '%s : %s' % (self.issue.key, self.get_summary())

    def prin_indent(self):
        return __PRINT_IDENT__.get(self.get_issue_type(), '   ')

    def assert_item_type(self, expected_type: Union[str, List[str]]):
        issue_type = self.get_issue_type()
        if issue_type in expected_type:
            return True
        raise Exception('Error! Unexpected item type. Current type: %s, expected type: ' % issue_type, expected_type)

    def print_basic_info(self):
        print(self.prin_indent(), self.key, self.get_issue_type(), self.get_summary())

    def get_status(self) -> str:
        return self.fields.status.name

    def change_status(self, status: str):
        self.assert_item_type([ISSUE_TYPE_CA_ITEM, ISSUE_TYPE_EPIC_ITEM])
        current_status = self.get_status()
        if not ((current_status == status) or
                (current_status in [STATUS_OBSOLETE, STATUS_DONE] and status != STATUS_OPEN)):
            if self.get_issue_type() == ISSUE_TYPE_CA_ITEM:
                epics = self.jira_op.get_epic_for_ca(self)
                if epics:
                    for epic in epics:
                        epic.change_status(status)

            if status == STATUS_DONE:
                # Add Remaining Effort to worklog if not 0
                r_effort = self.get_remaining_effort_in_seconds()
                if r_effort != 0:
                    self.add_worklog(spent_effort_in_seconds=r_effort)

                # Set End FB to current FB
                current_fb = get_current_fb()
                if int(self.get_end_fb()) > int(current_fb):
                    self.update_end_fb(current_fb)
            elif status == STATUS_OBSOLETE:
                if self.get_time_spent():
                    self.clear_remaining_effort()
                else:
                    self.update_original_estimate(0)

            self.jira_op.change_issue_status_by_key(self.key, status)

    def get_issue_type(self) -> str:
        return self.fields.issuetype.name

    def get_assignee(self) -> Optional[UserInfo]:
        if self.issue.raw['fields']['assignee']:
            return UserInfo(self.issue.raw['fields']['assignee'])
        else:
            return None

    def update_assignee(self, assignee_account: str):
        self.issue.update({'assignee': {'name': assignee_account}})

    def get_reporter(self) -> Optional[UserInfo]:
        if self.issue.fields.reporter:
            return UserInfo(self.issue.fields.reporter.raw)
        else:
            return None

    def get_contact_persons(self) -> List[UserInfo]:
        if self.issue.raw['fields']['customfield_38698']:
            persons = []
            for contact_person in self.issue.raw['fields']['customfield_38698']:
                persons.append(UserInfo(contact_person))
            return persons
        return []

    # noinspection PyTypeChecker
    def get_contact_person_display_name(self) -> str:
        contact_persons = self.get_contact_persons()
        if contact_persons:
            names = []
            for contact_person in contact_persons:
                names.append(contact_person.display_name)
            if len(names) > 1:
                return ','.join(names)
            else:
                return names[0]
        return ''

    def update_contact_person(self, contact_person_account: str):
        self.issue.update({'customfield_38698': [{'name': contact_person_account}]})

    def get_summary(self) -> str:
        return self.fields.summary

    # noinspection PyTypeChecker
    def update_summary(self, summary: str):
        self.issue.update(summary=summary)

    def get_labels(self) -> List[str]:
        labels = []
        if self.fields.labels:
            for label in self.fields.labels:
                labels.append(label)
        return labels

    def get_feature_id(self):
        return str(self.issue.raw['fields']['customfield_37381'])

    def get_fot_leader(self) -> Optional[UserInfo]:
        if self.issue.raw['fields']['customfield_38695']:
            return UserInfo(self.issue.raw['fields']['customfield_38695'])
        else:
            return None

    def get_requirement_area_owner(self) -> Optional[UserInfo]:
        if self.issue.raw['fields']['customfield_43891']:
            return UserInfo(self.issue.raw['fields']['customfield_43891'])
        else:
            return None

    def get_fs_status(self) -> str:
        return str(self.issue.raw['fields']['customfield_38705'])

    def get_fp_product(self) -> str:
        return str(self.issue.raw['fields']['customfield_38706'])

    def get_planned_system_release(self) -> str:
        if self.issue.raw['fields']['customfield_38724']:
            for release in self.issue.raw['fields']['customfield_38724']:
                return release['value']
        return ''

    def update_planned_system_release(self, release: str):
        self.issue.update({'customfield_38724': [{'value': release}]})

    def get_target_release(self) -> str:
        return str(self.issue.raw['fields']['customfield_38710'])

    def get_contributing_system_release(self) -> List[str]:
        if self.issue.raw['fields']['customfield_45090']:
            releases = []
            for release in self.issue.raw['fields']['customfield_45090']:
                releases.append(release['value'])
            return releases
        return []

    def get_requirement_area(self) -> str:
        return str(self.issue.raw['fields']['customfield_38712'])

    def update_requirement_area(self, requirement_area: str):
        self.issue.update({'customfield_38712': requirement_area})

    def get_domain(self) -> str:
        domain_map = {
            'siso': 'SiSo',
            'siso, deployment & configurations': 'SiSo',
            'callhandling': 'CallHandling',
            'qosenduserexp': 'QoSEndUserExp',
            'radioperf': 'RadioPerf',
            'perfcapa': 'PerfCapa',
            'cloud': 'Cloud',
            'e2eoperability-rnl': 'E2EOperability-RNL',
            'e2eoperability-basic': 'E2EOperability-Basic',
            'e2eoperability-sec': 'E2EOperability-Sec',
            'e2eoperability-sync': 'E2EOperability-Sync',
            'e2etrs-basic': 'E2ETrs-Basic',
            'e2etrs-sec': 'E2ETrs-Sec',
            'bb_hwi': 'BB_HWI',
            'ru_hwi': 'RU_HWI',
            'smallcells': 'SmallCells',
            'small cell-mmw-ext': 'SmallCells',
            'airphone': 'Airphone',
            'e2e transport': 'E2ETrs-Basic',
            'small cells': 'SmallCells',
            'call handling, mobility, load balancing': 'CallHandling',
            'performance & capacity': 'PerfCapa',
            'radio performance': 'RadioPerf',
            'qos & end user experience': 'QoSEndUserExp',
            'e2e operability': 'E2EOperability-Basic',
            'new bb hw introduction': 'BB_HWI',
            'new ru hw introduction': 'RU_HWI',
            'qos, services, end user experience': 'QoSEndUserExp',
            'call handling, moblity, load balancing': 'CallHandling',
            'call handling, mobility & load balancing': 'CallHandling'
        }
        requirement_area = self.get_requirement_area()
        if requirement_area.lower() in domain_map.keys():
            return domain_map[requirement_area.lower()]
        else:
            print('WARNNING! Requirement Area is not valid', self.issue.key)
            return requirement_area

    def get_solution_area(self) -> str:
        if self.issue.raw['fields']['customfield_38711']:
            return str(self.issue.raw['fields']['customfield_38711'])
        else:
            return ''

    def get_long_lead_time(self) -> str:
        if self.issue.raw['fields']['customfield_38714']:
            return str(self.issue.raw['fields']['customfield_38714']['value'])
        else:
            return ''

    def get_start_fb(self) -> str:
        if self.issue.raw['fields']['customfield_38694']:
            return str(self.issue.raw['fields']['customfield_38694'])
        else:
            return ''

    def update_start_fb(self, start_fb: str):
        self.assert_item_type([ISSUE_TYPE_CA_ITEM, ISSUE_TYPE_EPIC_ITEM])
        if self.get_status() not in [STATUS_DONE, STATUS_OBSOLETE]:
            if self.get_issue_type() == ISSUE_TYPE_CA_ITEM:
                epics = self.jira_op.get_epic_for_ca(self)
                if epics:
                    for epic in epics:
                        if epic.get_status() not in [STATUS_DONE, STATUS_OBSOLETE]:
                            epic.update_start_fb(start_fb)
            self.issue.update({'customfield_38694': str(int(start_fb))})

    def get_end_fb(self) -> str:
        if self.issue.raw['fields']['customfield_38693']:
            return str(self.issue.raw['fields']['customfield_38693'])
        else:
            return ''

    def update_end_fb(self, end_fb: str):
        self.assert_item_type([ISSUE_TYPE_CA_ITEM, ISSUE_TYPE_EPIC_ITEM])
        if self.get_status() not in [STATUS_DONE, STATUS_OBSOLETE]:
            if self.get_issue_type() == ISSUE_TYPE_CA_ITEM:
                epics = self.jira_op.get_epic_for_ca(self)
                if epics:
                    for epic in epics:
                        if epic.get_status() not in [STATUS_DONE, STATUS_OBSOLETE]:
                            epic.update_end_fb(end_fb)
            self.issue.update({'customfield_38693': str(int(end_fb))})

    def get_activity_type(self) -> str:
        if self.issue.raw['fields']['customfield_38750']:
            return self.issue.raw['fields']['customfield_38750']['value']
        return ''

    def update_activity_type(self, activity_type: str):
        self.issue.update({'customfield_38750': {'value': activity_type}})

    def get_sub_activity_type(self) -> str:
        if self.issue.raw['fields']['customfield_38753']:
            return self.issue.raw['fields']['customfield_38753']['value']
        return ''

    def update_sub_activity_type(self, sub_activity_type: str):
        self.issue.update({'customfield_38753': {'value': sub_activity_type}})

    def get_competence_area(self):
        if self.issue.raw['fields']['customfield_38690']:
            return self.issue.raw['fields']['customfield_38690']['value']
        return ''

    def get_item_id(self):
        # print(self.issue.raw['fields']['customfield_38702'])
        if self.issue.raw['fields']['customfield_38702']:
            return self.issue.raw['fields']['customfield_38702']
        return ''

    def update_item_id(self, item_id: str):
        self.issue.update({'customfield_38702': item_id})

    def get_logged_effort(self) -> int:
        if self.issue.raw['fields']['customfield_43290']:
            return int(self.issue.raw['fields']['customfield_43290'])
        return 0

    def get_time_spent(self) -> int:
        if self.issue.raw['fields']['aggregatetimespent']:
            e = int(self.issue.raw['fields']['aggregatetimespent'])
            if e != 0:
                return int(e / 3600)

        return self.get_logged_effort()

    def get_original_estimate(self) -> int:
        self.assert_item_type([ISSUE_TYPE_CA_ITEM, ISSUE_TYPE_EPIC_ITEM])
        if self.issue.raw['fields']['customfield_43292']:
            return int(self.issue.raw['fields']['customfield_43292'])
        return 0

    def update_original_estimate(self, effort_estimate: int):
        self.assert_item_type([ISSUE_TYPE_CA_ITEM, ISSUE_TYPE_EPIC_ITEM])
        self.issue.update(fields={"timetracking": {"originalEstimate": "%sh" % effort_estimate}})

    def get_remaining_effort(self) -> int:
        if self.issue.raw['fields']['customfield_43291']:
            return int(self.issue.raw['fields']['customfield_43291'])
        return 0

    def get_remaining_effort_in_seconds(self) -> int:
        # print(self.issue.raw['fields']['aggregatetimeestimate'])
        if self.issue.raw['fields']['aggregatetimeestimate']:
            return int(self.issue.fields.aggregatetimeestimate)
            # return int(self.issue.raw['fields']['aggregatetimeestimate'])
        return 0

    def add_worklog(self, spent_effort_hours: int = 0, spent_effort_in_seconds: int = 0):
        if spent_effort_hours:
            self.jira_op.jira.add_worklog(self.key, timeSpentSeconds=spent_effort_hours * 3600)
        elif spent_effort_in_seconds:
            self.jira_op.jira.add_worklog(self.key, timeSpentSeconds=spent_effort_in_seconds)

    def clear_remaining_effort(self):
        self.jira_op.jira.add_worklog(self.key, timeSpentSeconds=3600, adjustEstimate='new', newEstimate=0)

    def get_system(self) -> str:
        self.assert_item_type([ISSUE_TYPE_CUSTOMER_FEATURE, ISSUE_TYPE_INTERNAL_FEATURE])
        if self.issue.raw['fields']['customfield_38699']:
            systems = []
            for system in self.issue.raw['fields']['customfield_38699']:
                systems.append(system['value'])
            return ','.join(systems)
        return ''

    def get_delay_reason(self) -> str:
        self.assert_item_type(ISSUE_TYPE_CA_ITEM)
        if self.issue.raw['fields']['customfield_38759']:
            delay_reasons = []
            for reason in self.issue.raw['fields']['customfield_38759']:
                delay_reasons.append(reason['value'])
            return ','.join(delay_reasons)
        return ''

    def update_delay_reason(self, delay_reason: str):
        self.assert_item_type(ISSUE_TYPE_CA_ITEM)
        self.issue.update({'customfield_38759': [{'value': delay_reason}]})

    def get_delay_explain(self) -> str:
        self.assert_item_type(ISSUE_TYPE_CA_ITEM)
        if self.issue.raw['fields']['customfield_38755']:
            return self.issue.raw['fields']['customfield_38755']
        return ''

    def update_delay_explain(self, delay_explain: str):
        self.assert_item_type(ISSUE_TYPE_CA_ITEM)
        self.issue.update({'customfield_38755': delay_explain})

    def get_work_type(self) -> str:
        if self.issue.raw['fields']['customfield_38368']:
            return self.issue.raw['fields']['customfield_38368']['value']
        return ''

    def update_work_type(self, work_type: str):
        self.issue.update({'customfield_38368': {'value': work_type}})

    def get_fb_commit_status(self):
        if self.issue.raw['fields']['customfield_38758']:
            return self.issue.raw['fields']['customfield_38758']['value']
        return ''

    def update_fb_commit_status(self, commit: Union[bool, str] = ''):
        if commit in [FB_NOT_COMMITTED, FB_COMMITTED]:
            self.issue.update({'customfield_38758': {'value': commit}})
        elif commit:
            self.issue.update({'customfield_38758': {'value': FB_COMMITTED}})
        elif not commit:
            self.issue.update({'customfield_38758': {'value': FB_NOT_COMMITTED}})

    def get_release_commit_status(self):
        if self.issue.raw['fields']['customfield_38728']:
            return self.issue.raw['fields']['customfield_38758']['value']
        return ''

    def update_release_commit_status(self, commit: Union[bool, str]):
        if commit:
            self.issue.update({'customfield_38728': {'value': RELEASE_COMMIT_BEFORE}})
        elif not commit:
            self.issue.update({'customfield_38728': {'value': RELEASE_NOT_COMMITTED}})
        else:
            self.issue.update({'customfield_38728': {'value': commit}})

    def get_epic_team_id(self) -> int:
        self.assert_item_type(ISSUE_TYPE_EPIC_ITEM)
        if self.issue.raw['fields']['customfield_29790']:
            return int(self.issue.raw['fields']['customfield_29790'])
        else:
            return 0

    def update_epic_team(self, team_id: int):
        self.issue.update({'customfield_29790': '%d' % team_id})

    def get_last_comment(self):
        num_of_comment = len(self.fields.comment.comments)
        if num_of_comment == 0:
            return ''

        comment_id = self.fields.comment.comments[num_of_comment - 1].id
        comment = self.jira_op.jira.comment(self.key, comment_id)

        return str(comment.created).split('T')[0] + " " + comment.body


class JiraOp:
    def __init__(self):
        self.jira = init_jira_connection()
        self.feature_fields = ['key', 'status', 'summary', 'assignee', 'reporter', 'issuetype', 'issuelinks',
                               'customfield_37381',  # Feature ID
                               'customfield_38695',  # FOT leader
                               'customfield_38698',  # Contact Person
                               'customfield_43891',  # RAO
                               'customfield_38705',  # FS Status
                               'customfield_38706',  # FP Products
                               'customfield_38724',  # Planned System Release
                               'customfield_38710',  # Target Release
                               'customfield_38712',  # Requirement Area
                               'customfield_38711',  # Solution Area
                               'customfield_43190',  # FS1TA required
                               'customfield_38714',  # Long Lead Time
                               'customfield_38702',  # Item ID
                               'customfield_45090'  # Contributing System Release
                               ]
        self.system_item_fields = ['key', 'status', 'summary', 'assignee', 'reporter', 'issuetype',
                                   'customfield_38702'  # Item ID
                                   ]
        self.entity_item_fields = ['key', 'status', 'summary', 'assignee', 'reporter', 'issuetype',
                                   'customfield_38702'  # Item ID
                                   ]
        self.ca_item_fields = ['key', 'status', 'summary', 'assignee', 'reporter', 'issuetype',
                               'aggregatetimespent',  # Time Spent
                               'aggregatetimeestimate',  # Remaining time estimate in second
                               'aggregatetimeoriginalestimate',  # Original Estimate
                               'timeestimate',  # Remaining Estimate
                               'customfield_38698',  # Contact Person
                               'customfield_38712',  # Requirement Area
                               'customfield_37381',  # Feature ID
                               'customfield_38693',  # End FB
                               'customfield_38694',  # Start FB
                               'customfield_31591',  # Target End
                               'customfield_38758',  # FB committed status
                               'customfield_38750',  # Activity Type
                               'customfield_38753',  # Sub-activity Type
                               'customfield_38702',  # Item ID
                               'customfield_38759',  # Delay reason
                               'customfield_43290',  # Logged Effort (h)
                               'customfield_43292',  # Original Estimate
                               'customfield_43291',  # Remaining Effort
                               'customfield_38724',  # Planned System Release
                               'customfield_38690',  # Competence Area
                               'customfield_38755'  # Delay explanation
                               ]

        self.epic_item_fields = ['key', 'status', 'summary', 'assignee', 'reporter', 'issuetype',
                                 'aggregatetimespent',  # Time Spent
                                 'aggregatetimeoriginalestimate',  # Original Estimate
                                 'aggregatetimeestimate',  # Remaining time estimate in second
                                 'timeestimate',  # Remaining Estimate
                                 'customfield_38698',  # Contact Person
                                 'customfield_37381',  # Feature ID
                                 'customfield_38693',  # End FB
                                 'customfield_38694',  # Start FB
                                 'customfield_31591',  # Target End
                                 'customfield_38758',  # FB committed status
                                 'customfield_38750',  # Activity Type
                                 'customfield_38753',  # Sub-activity Type
                                 'customfield_38702',  # Item ID
                                 'customfield_38759',  # Delay reason
                                 'customfield_43290',  # Logged Effort (h)
                                 'customfield_43292',  # Original Estimate
                                 'customfield_43291',  # Remaining Effort
                                 'customfield_29790'  # Teams
                                 ]

    def __del__(self):
        if hasattr(self, 'jira'):
            self.jira.close()

    def item(self, issue_key: str, fields=None, expand='') -> Optional[Item]:
        try:
            if fields:
                return Item(self.jira.issue(issue_key, fields=str_l(fields), expand=expand), self)
            else:
                return Item(self.jira.issue(issue_key, expand=expand), self)
        except JIRAError as e:
            print(e.text, issue_key)
            return None

    # noinspection PyTypeChecker
    def search_items(self, jql_str: str, fields=None, max_results: int = 0, expand='') -> List[Item]:
        issues = self.jira.search_issues(jql_str=jql_str, maxResults=max_results, validate_query=True,
                                         fields=str_l(fields),
                                         expand=expand)
        if issues:
            items = []
            for issue in issues:
                items.append(Item(issue, jira_op=self))
            return items
        return []

    def search_feature_items(self, jql_str: str, max_results: int = 0, expand='') -> List[Item]:
        return self.search_items(jql_str,
                                 fields=self.feature_fields,
                                 max_results=max_results,
                                 expand=expand)

    def search_ca_items(self, jql_str: str, max_results: int = 0,
                        include_comments: bool = False, expand='') -> List[Item]:
        item_fields = self.ca_item_fields
        if include_comments:
            item_fields.append('comment')
        return self.search_items(jql_str,
                                 fields=item_fields,
                                 max_results=max_results,
                                 expand=expand)

    def search_epic_items(self, jql_str: str, max_results: int = 0, expand='') -> List[Item]:
        return self.search_items(jql_str,
                                 fields=self.epic_item_fields,
                                 max_results=max_results,
                                 expand=expand)

    def change_issue_status_by_key(self, issue_key: str, status: str) -> None:
        transitions_map = {
            STATUS_SCHEDULED: TRANSITION_NAME_STATUS_SCHEDULED,
            STATUS_INPROGRESS: TRANSITION_NAME_STATUS_INPROGRESS,
            STATUS_DONE: TRANSITION_NAME_STATUS_DONE,
            STATUS_OPEN: TRANSITION_NAME_STATUS_OPEN,
            STATUS_OBSOLETE: TRANSITION_NAME_STATUS_OBSOLETE
        }
        transitions = self.jira.transitions(issue_key)
        for t in transitions:
            # print(issue_key, t['name'], transitions_map[status])
            if t['name'] == transitions_map[status]:
                self.jira.transition_issue(issue_key, t['id'])
                return
        # print('Change not change status:', issue_key, status)

    def change_issue_status(self, issue: Issue, status: str) -> None:
        if issue.fields.status.name != status:
            self.change_issue_status_by_key(issue.key, status)

    def get_epic_for_ca(self, ca_item: Item) -> List[Item]:
        ca_item.assert_item_type(ISSUE_TYPE_CA_ITEM)
        jql_str = 'issueFunction in portfolioChildrenOf("key = %s")' % ca_item.key
        return self.search_epic_items(jql_str)

    def get_new_epic_for_ca(self, ca_item: Item) -> Item:
        ca_item.assert_item_type(ISSUE_TYPE_CA_ITEM)
        current_status = ca_item.get_status()
        if current_status not in [STATUS_DONE, STATUS_OBSOLETE]:
            jql_str = 'issueFunction in portfolioChildrenOf("key = %s") AND issuetype = Epic ' \
                      'AND "Contact Person" is EMPTY' % ca_item.key
            epics = self.search_epic_items(jql_str, max_results=1)
            if len(epics):
                return epics[0]
            else:
                self.__create_epic_for_ca__(ca_item)
                return self.get_new_epic_for_ca(ca_item)

        raise Exception('ERROR! Can not get nor create new EPIC item. Please check item status and your permission.',
                        current_status, ca_item)

    def get_new_ca_item(self, entity_item: Item, competence_area: str) -> Optional[Item]:
        entity_item.assert_item_type(ISSUE_TYPE_ENTITY_ITEM)
        current_status = entity_item.get_status()
        if current_status not in [STATUS_DONE, STATUS_OBSOLETE]:
            jql_str = 'issueFunction in portfolioChildrenOf("key=%s") ' \
                      'AND "Competence Area" = "%s" ' \
                      'AND issuetype = "Competence Area" ' \
                      'AND "Contact Person" is EMPTY' % (entity_item.key, competence_area)
            ca_items = self.search_items(jql_str)
            if len(ca_items):
                return ca_items[0]
            else:
                t_id = self.jira.find_transitionid_by_name(entity_item.key, TRANSITION_NAME_NEW_CA)
                if t_id:
                    self.jira.transition_issue(entity_item.key, TRANSITION_NAME_NEW_CA,
                                               fields={'customfield_38690': {'value': competence_area}})
                    return self.get_new_ca_item(entity_item, competence_area)
        raise Exception('ERROR! Can not get nor create new CA item. Please check item status and your permission.',
                        current_status, entity_item)

    def __create_epic_for_ca__(self, ca_item: Item):
        competence_area = ca_item.get_competence_area()
        if competence_area in ['RAN SysSpec1']:
            url = 'https://jiradc.ext.net.nokia.com/secure/ExocetOperation!default.jspa?operationId=368&sourceIssueId=%s' % ca_item.id
        elif competence_area in ['RAN SysSpec2']:
            url = 'https://jiradc.ext.net.nokia.com/secure/ExocetOperation!default.jspa?operationId=527&sourceIssueId=%s' % ca_item.id
        elif competence_area in ['RAN SysArch']:
            url = 'https://jiradc.ext.net.nokia.com/secure/ExocetOperation!default.jspa?operationId=520&sourceIssueId=%s' % ca_item.id
        elif competence_area in ['RAN SysSimu']:
            url = 'https://jiradc.ext.net.nokia.com/secure/ExocetOperation!default.jspa?operationId=519&sourceIssueId=%s' % ca_item.id
        elif competence_area in ['SRAN SysSpec']:
            url = 'https://jiradc.ext.net.nokia.com/secure/ExocetOperation!default.jspa?operationId=261&sourceIssueId=%s' % ca_item.id
        else:
            # RAN SysSpec1
            url = 'https://jiradc.ext.net.nokia.com/secure/ExocetOperation!default.jspa?operationId=368&sourceIssueId=%s' % ca_item.id
        self.jira._session.get(url=url)

    def create_internal_feature(self, feature_id: str, summary: str, description: str,
                                rao_account: str, label: str = ''):
        #   System: RAN
        issue_dict = {
            'project': {'id': 42894},  # Project 'Fusion Feature Backlog'
            'issuetype': {'name': ISSUE_TYPE_INTERNAL_FEATURE},
            'summary': summary,
            'description': description,
            'labels': [label],
            'customfield_37381': feature_id,  # Feature ID
            'customfield_38702': feature_id,  # Items ID
            'customfield_38368': {'value': 'Future Release Development'},  # Work Type
            'customfield_38699': [{'value': 'RAN'}],  # System
            'customfield_43891': {'name': rao_account}  # Requirement Area Owner
        }
        issue = self.jira.create_issue(fields=issue_dict)
        return Item(issue)

    def create_system_item(self, feature_item: Item) -> Optional[Item]:
        feature_item.assert_item_type([ISSUE_TYPE_CUSTOMER_FEATURE, ISSUE_TYPE_INTERNAL_FEATURE])
        t_id = self.jira.find_transitionid_by_name(feature_item.key, TRANSITION_NAME_NEW_SI)
        if t_id:
            jql_str = 'issueFunction in portfolioChildrenOf("key=%s") AND issuetype = "System Item" ' % feature_item.key
            existing_system_items = self.search_items(jql_str)
            self.jira.transition_issue(feature_item.key, TRANSITION_NAME_NEW_SI)
            system_items = self.search_items(jql_str)
            if len(system_items):
                for system_item in system_items:
                    if system_item not in existing_system_items:
                        return system_item
        raise Exception('ERROR! Can not get nor create new system item. Please check item status and your permission.',
                        feature_item.get_status(), feature_item)

    def create_entity_item(self, system_item: Item) -> Optional[Item]:
        system_item.assert_item_type(ISSUE_TYPE_SYSTEM_ITEM)
        t_id = self.jira.find_transitionid_by_name(system_item.key, TRANSITION_NAME_NEW_EI)
        if t_id:
            jql_str = 'issueFunction in portfolioChildrenOf("key=%s") AND issuetype = "Entity Item" ' % system_item.key
            existing_entity_items = self.search_items(jql_str)
            self.jira.transition_issue(system_item.key, TRANSITION_NAME_NEW_EI)
            entity_items = self.search_items(jql_str)
            if len(entity_items):
                for entity_item in entity_items:
                    if entity_item not in existing_entity_items:
                        return entity_item
        raise Exception('ERROR! Can not get nor create new entity item. Please check item status and your permission.',
                        system_item.get_status(), system_item)




if __name__ == '__main__':
    running = True
    while running:
        try:
            jira = JiraOp()
            key_list = ['FCA_RAN_SYSSIMU-72', 'FCA_RAN_SYSSIMU-77', 'FPB-724133']
            running = False
            for issue_key in key_list:
                item = jira.item(issue_key)
                assignee = item.get_assignee()
                print(assignee.name)
        except Exception as err:
            print(err)

    # print(jira.get_assignee(jira.get_issue_by_key('FPB-696805', fields=['assignee'])))
    # item = jira.item('FFB-32878')
    # item = jira.item('FCA_RAN_SYSSPEC2-639')
    # item.set_remaining_effort(81)
    # print(item.issue.raw['fields']['aggregatetimeestimate'])
    # result = item.get_remaining_effort_in_seconds()
    # print(result)
